

import sys, os, re
import time

import urllib2
from urllib2 import urlopen, HTTPError
import StringIO
from lxml import etree
from dateutil import parser as dateparser
import pprint
import re
from baseHandler import BaseProxyHandler, basehandler
import string
from time import strftime
from datetime import datetime
import datetime as dtmod
from datetime import timedelta
from isodate import parse_duration
import simplejson

from mod_python import apache

def now():
    return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

class GitHubHandler(BaseProxyHandler):

    def __init__(self, d):
        self.resType = ''
        BaseProxyHandler.__init__(self, d)
        



    def fetch_json(self, uri, req):
        
        try:
            fh = urlopen(uri);
        except HTTPError, e:
            return self.error("couldn't retrieve github data from %s" % uri, req, status=404)

        json = []
        #try:
        #    ureq = urllib2.request(uri, headers=self.hdrs)
        #    hdlr = urllib2.httpredirecthandler()
        #    opener = urllib2.build_opener(hdlr)
        #    fh = opener.open(ureq)
        #except httperror, e:
        #    sys.stderr.write(repr(e))
        #    sys.stderr.flush();
        #    return self.error("couldn't retrieve github data from %s" % uri, req, status=404)

        try:
            json = simplejson.load(fh)
        except:
            return self.error("Response from GitHub (%s) not parsable" % uri, req, status=500)

        fh.close()
        return json


    def fetch_changes(self, req, requri, dt=None):
        changes = []
        valid = re.compile('^http[s]*://github.com/(.+)/(.+)')
        match = valid.match(requri)
        if match is None or string.find( match.group(1), '/') >= 0 or string.find( match.group(2), '/') >= 0:
            return changes

        self.hdrs = {'Host' : match.group()[7:],
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language' : 'en-us,en;q=0.5',
            'Proxy-Connection' : 'keep-alive',
            'Pragma' : 'no-cache',
            'Cache-Control' : 'no-cache',
            'User-Agent' : 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2'}

        
        username = match.group(1);
        repo = match.group(2);

        url = "https://api.github.com/repos/%s/%s/commits" % (username, repo);

        json = self.fetch_json(url, req)
        if json == None:
            return {}

        mementoUrl = "https://github.com/%s/%s/tree/%s"

        dtobj = None
        for commits in json:
                info = {}
                try: info['dcterms:creator'] = commits['commit']['author']['name']
                except: pass
                info['type'] = 'valid'
                info['last'] = dtobj
                dtobj = dateparser.parse(commits['commit']['author']['date'])
                # unknown usage... but likely loads
                info['sha'] = commits['sha']
                changes.append((dtobj, mementoUrl % (username, repo, commits['sha']), info))                
                
        if changes:
            changes.sort()
            changes[-1][-1]['last'] = 'now'

        if changes and self.resType == 'tree':
            self.fetch_tree(req, changes, dt, username, repo)
            return {}
        elif changes and self.resType == 'diff':
            self.fetch_diff(req, changes, dt, username, repo)
            return {}
        else:
            return changes



    def fetch_diff(self, req, changes, dt, username, repo):
        jsonWhenForked = self.fetch_tree(req, changes, dt, username, repo, True);

        nowd = now();
        current = dateparser.parse(nowd)
        jsonNow = self.fetch_tree(req, changes, current, username, repo, True);

        #self.display_tree(req, jsonNow)
        #self.display_tree(req, jsonWhenForked)

        added = []
        deleted = []

        html = '''
            <html>
                <head>
                    <title>Diff</title>
                </head>
                <body>
                    <p>The following files were added in the latest version</p>
        '''
        addedTemplate = "<div style='color: green'>%s</div>"   


        for nowFile in jsonNow:
            found = False;
            for oldFile in jsonWhenForked:
                if( nowFile['path'] == oldFile['path'] ):
                    found = True;

            if not found:
                added.append(nowFile['path'])
                html = html + addedTemplate % nowFile['path'];

        html = html + '<p>The following files were removed from the latest version</p>'
        
        deletedTemplate = "<div style='color: red'>%s</div>"   

        for oldFile in jsonWhenForked:
            found = False;
            for nowFile in jsonNow:
                if( nowFile['path'] == oldFile['path'] ):
                    found = True;

            if not found:
                deleted.append(oldFile['path'])
                html = html + deletedTemplate % oldFile['path']

        html = html + "</table></body></html>";

        req.status = 200
        req.content_type = 'text/html'
        req.send_http_header()
        req.write(html)

    def fetch_tree( self, req, changes, wanted, username, repo, ret=False ):
        
        if changes:
            first = changes[0]
            last = changes[-1]
        else:
            first = None
            last = None

        next = None
        prev = None

        if wanted < first[0]:
            loc = first
            next = changes[1]
        elif wanted > last[0]:
            loc = last
            prev = changes[-1]
        else:
            tdiff = lambda y,x: float(abs((y-x).days * 86400) + abs((y-x).seconds))
            # Else find closest
            for c in range(1, len(changes)):
                this = changes[c]
                if wanted < this[0] or c == len(changes)-1:
                    llast = changes[c-1]
                    tdelta1 = tdiff(llast[0], wanted)
                    tdelta2 = tdiff(this[0], wanted)

                    if tdelta1 < tdelta2:
                        # Closest Memento to request is previous
                        loc = llast
                        if (c-2 >= 0):
                            prev = changes[c-2]
                        next = this
                    else:
                        loc = this
                        prev = llast
                        if (c < len(changes)-1):
                            next = changes[c+1]
                    break
          
        url = 'https://api.github.com/repos/%s/%s/git/trees/%s?recursive=1' % (username, repo, loc[2]['sha']) 
        json = self.fetch_json(url, req)
        if json == None:
            self.error("Resource not in archive: %s" % uri, req, status=404)
            return {}

        if not ret:
            self.display_tree(req, json['tree'], username, repo, json['sha'])
        else:
            return json['tree']


    def display_tree( self, req, tree, username, repo, sha ):

        html = '''
            <html>
                <head>
                    <title>Tree</title>
                </head>
                <body>
                    <table>
                        <tr><th>File Name</th><th>Size</th></tr>
        '''
        filepath = "<tr><td><a href=%s>%s</a></td><td>%s</td></tr>"               

        fileTemplate = "https://github.com/%s/%s/blob/%s/%s";


        for file in tree:
            size = ''
            try:
                size = file['size']
            except:
                size = ''
            fileURL = fileTemplate % ( username, repo, sha, file['path'] ) 
            html = html + filepath % (fileURL, file['path'], size)
            

        html = html + "</table></body></html>";

        req.status = 200
        req.content_type = 'text/html'
        req.send_http_header()
        req.write(html)
        return {}

def handler(req):
    urlOffset = len('/git')

    if req.uri[urlOffset:urlOffset+len('/timebundle/')] == '/timebundle/':
        hdlr = GitHubHandler('git')
        return basehandler(req, hdlr)
    elif req.uri[urlOffset:urlOffset+len('/timemap/')] == '/timemap/':
        hdlr = GitHubHandler('git')
        return basehandler(req, hdlr)
    elif req.uri[urlOffset:urlOffset+len('/timegate/')] == '/timegate/':
        hdlr = GitHubHandler('git')
        return basehandler(req, hdlr)
    elif req.uri[urlOffset:urlOffset+len('/tree/')] == '/tree/':
        hdlr = GitHubHandler('git/tree')
        hdlr.resType = 'tree'
        return basehandler(req, hdlr)
    elif req.uri[urlOffset:urlOffset+len('/diff/')] == '/diff/':
        hdlr = GitHubHandler('git/diff')
        hdlr.resType = 'diff'
        return basehandler(req, hdlr)


if __name__ == '__main__':
    hdlr = GitHubHandler('aggr')
    start = time.time()
    c = hdlr.fetch_changes(None, sys.argv[1])
    end = time.time() - start
    pprint.pprint(c)
    print "Fetch %s revs in %s seconds" % (len(c), end)

